/**
* @file drv_key.c
* @brief  key scan
* @details 
* @author huangbin
* @version V1.0.0
* @date 2025-04-20
* @copyright All documents are copyrighted by Shenzhen Xinlongwei Technology Co., LTD.
*            They may not be used for commercial purposes without permission
*/
#include "inc.h"

#define KEY_SCAN_PERIOD_MS			200	//按键扫描去抖动周期,以ms为单位
#define MSG_KEYDOWN 0
#define MSG_KEYUP   1

typedef enum
{
	STA_NO_KEY,						//没有按键按下
	STA_SCAN_DELAY,					//延时检测状态，防抖
	STA_KEY_DOWN,					//有按键按下
}KEYSTATUS;

typedef	struct tagKeyBoard 
{
	KEYSTATUS		KeyStatus;				//键盘状态
	uint32_t		LastScanCode;			//上一次的键值
	uint32_t		KeyDownTime;			//按键按下的时间
	uint32_t		KeyScanCount;			//按键扫描计数
} KEYDATA;

static KEYDATA Keydata;

void KeyInit(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	memset(&Keydata,0,sizeof(Keydata));
	Keydata.KeyStatus = STA_NO_KEY;
   
    
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	
	GPIO_Init(GPIOC, &GPIO_InitStructure);
    
}
uint32_t HardWareKeyScan(void)
{
	uint32_t ScanCode;
	
    ScanCode = GPIOC->IDR;
    ScanCode = ~ScanCode;
    ScanCode &= 0x000F; //0-3
	
	return ScanCode;
}
//统计有多少个位的值为1
uint32_t Getbit1Count(uint32_t value)
{	
	value = value - ((value >> 1) & 0x55555555);
	value = (value & 0x33333333) + ((value & 0xCCCCCCCC) >> 2);
	value = (value + (value >> 4)) & 0x0F0F0F0F;
	value = value + (value >> 8);
	value = value + (value >> 16);
	return value & 0x3F;
}
void SendKeyMessage(uint32_t MessageType,uint32_t ScanCode);
void KeyScan(uint16_t ms)
{
	uint32_t ScanCode = 0;
	
	if (Keydata.KeyStatus == STA_KEY_DOWN)
	{
        Keydata.KeyScanCount += ms;
		if (Keydata.KeyScanCount >= KEY_SCAN_PERIOD_MS)
		{
			Keydata.KeyScanCount = 0;
            if(Keydata.KeyDownTime < 0xFFFFFFFF - KEY_SCAN_PERIOD_MS)
                Keydata.KeyDownTime += KEY_SCAN_PERIOD_MS;
			ScanCode = HardWareKeyScan();
			if (ScanCode)
			{
				if (Getbit1Count(ScanCode) == 1) //只允许一个按键同时按下
				{					
					if (ScanCode & Keydata.LastScanCode)
					{
						//发送key down 消息
						SendKeyMessage(MSG_KEYDOWN,ScanCode);
//						DebugPrintf("key down 2 = 0x%x\r\n",ScanCode);
					}
					else 
					{
						//有两个键同时按下，先按的键已弹起
						SendKeyMessage(MSG_KEYUP,Keydata.LastScanCode);
//						DebugPrintf("key up 2 = 0x%x\r\n",ScanCode);
						Keydata.KeyStatus = STA_SCAN_DELAY;
						Keydata.LastScanCode = ScanCode;
					}
				}
			}
			else
			{				
				//发送key up 消息
				SendKeyMessage(MSG_KEYUP,Keydata.LastScanCode);
//				DebugPrintf("key up = 0x%x\r\n",ScanCode);
				Keydata.KeyStatus = STA_NO_KEY;
				Keydata.LastScanCode = 0;
				Keydata.KeyDownTime = 0;
			}
		}
	}
	else
	{
		if (++Keydata.KeyScanCount > 3)
		{
			ScanCode = HardWareKeyScan();
			if (Keydata.KeyStatus == STA_SCAN_DELAY)
			{
				Keydata.KeyScanCount = 0;
				if (ScanCode == Keydata.LastScanCode)
				{
					//发送key down 消息
					SendKeyMessage(MSG_KEYDOWN,ScanCode);
//					DebugPrintf("key down 1 = 0x%x\r\n",ScanCode);
					Keydata.KeyStatus = STA_KEY_DOWN;
					Keydata.KeyScanCount = 0;
				}
				else
				{
					Keydata.KeyStatus = STA_NO_KEY;
				}
			}
			else if (Keydata.KeyStatus == STA_NO_KEY)
			{
				if (ScanCode)
				{
					if (Getbit1Count(ScanCode) == 1)		//只有一个键按下才有效
					{
						Keydata.KeyStatus = STA_SCAN_DELAY;
						Keydata.LastScanCode = ScanCode;
					}
				}
			}
		}
	}
}
//按键物理键值与逻辑键值对应表
const uint16_t ScanCodeToKeyCode[] = 
{
	//逻辑键值
	VK_ESC,
	VK_ENTER,
	VK_DOWN,
	VK_UP,
	VK_NULL,
	VK_NULL,
	VK_NULL,
    VK_NULL,
};
#include "gui_core.h"
void SendKeyMessage(uint32_t MessageType,uint32_t ScanCode)
{
	if(MessageType == MSG_KEYDOWN)
	{
        DebugPrintf("===Key down: %d\r\n",Getbit1Count(ScanCode-1));
		gui_send_key_evt(guiGetInstance(),GUI_MSG_KEY_EVT_DOWN,ScanCodeToKeyCode[Getbit1Count(ScanCode-1)]);
	}
	else
	{
        DebugPrintf("===Key up: %d\r\n",Getbit1Count(ScanCode-1));
		gui_send_key_evt(guiGetInstance(),GUI_MSG_KEY_EVT_UP,ScanCodeToKeyCode[Getbit1Count(ScanCode-1)]);
	}
}

